package menu

import (
	"strconv"
	"strings"
	"unicode/utf8"

	"github.com/gin-gonic/gin"
	"gorm.io/gorm"

	"github.com/super-team/start-kit/handler/response"
	"github.com/super-team/start-kit/models"
	"github.com/super-team/start-kit/pkg/casbin"
	"github.com/super-team/start-kit/pkg/mysql"
	"github.com/super-team/start-kit/tools/log"
)

func Route(r *gin.RouterGroup, middlewares ...gin.HandlerFunc) {

	g := r.Group("/menu", middlewares...)
	{
		g.POST("", addMenu)
		g.PUT("", updateMenu)
		g.DELETE("", deleteMenu)
		g.GET("/tree", getMenuTree)
		g.GET("/list", getMenuList)
		g.GET("/info", getMenu)
	}
}

// @Summary 获取菜单树
// @Description 获取菜单树
// @Tags menu
// @Accept  json
// @Product json
// @Param Authorization header string false "Bearer 用户令牌"
// @Success 200 {string} string	"{"code": 200, "message": "获取菜单树成功","data":"[]"}"
// @Success 200 {string} string	"{"code": -1, "message": "服务器异常"}"
// @Router /api/menu/tree [get]
func getMenuTree(ctx *gin.Context) {
	list, err := models.GetMenuTree(ctx)
	if err != nil {
		response.Error(ctx, err.Error())
		return
	}
	response.Success(ctx, "获取菜单树成功", list)
}

type request struct {
	Visible *int   `json:"visible" form:"visible"`
	Title   string `json:"title" form:"title"`
}

// @Summary 获取菜单列表
// @Description 获取菜单列表
// @Tags menu
// @Accept  json
// @Product json
// @Param visible query int false "状态"
// @Param title query string false "名称"
// @Param Authorization header string false "Bearer 用户令牌"
// @Success 200 {string} string	"{"code": 200, "message": "获取菜单列表成功","data":"[]"}"
// @Success 200 {string} string	"{"code": -1, "message": "服务器异常"}"
// @Router /api/menu/list [get]
func getMenuList(ctx *gin.Context) {
	request := new(request)
	if err := ctx.ShouldBindQuery(request); err != nil {
		response.Error(ctx, "参数错误")
		return
	}
	if request.Visible != nil && *request.Visible < 0 {
		request.Visible = nil
	}
	list, err := models.GetMenuList(ctx, request.Title, request.Visible)
	if err != nil {
		response.Error(ctx, err.Error())
		return
	}
	response.Success(ctx, "获取菜单列表成功", list)
}

// @Summary 获取menu数据
// @Description 获取menu数据
// @Tags menu
// @Param Authorization header string false "Bearer 用户令牌"
// @Param id path int true "id"
// @Success 200 {string} string "{"code": 200, "data": [...]}"
// @Success 200 {string} string "{"code": -1, "message": "参数错误"}"
// @Router /api/menu/info [get]
func getMenu(ctx *gin.Context) {
	id := ctx.Query("id")
	idInt, err := strconv.ParseInt(id, 10, 64)
	if err != nil {
		response.Error(ctx, "参数错误")
		return
	}
	m, err := models.GetMenuByID(ctx, idInt)
	if err != nil {
		response.Error(ctx, "参数错误")
		return
	}
	if m.ID == 0 {
		response.Error(ctx, "暂无该菜单数据")
		return
	}
	response.Success(ctx, "获取菜单成功", m)
}

type addRequest struct {
	Action     *string `json:"action"`
	Component  *string `json:"component"`
	Icon       *string `json:"icon" binding:"required"`
	IsFrame    *int    `json:"isFrame"`
	MenuName   *string `json:"menuName"`
	MenuType   *string `json:"menuType" binding:"required"`
	ParentId   *int    `json:"parentId" binding:"required"`
	Path       *string `json:"path"`
	Sort       *int    `json:"sort" binding:"required"`
	Title      *string `json:"title" binding:"required"`
	Visible    *int    `json:"visible"`
	Permission *string `json:"permission"`
}

// @Summary 创建菜单
// @Description 创建菜单
// @Tags menu
// @Accept  json
// @Product json
// @Param Authorization header string false "Bearer 用户令牌"
// @Param data body addRequest true "body"
// @Success 200 {string} string	"{"code": 200, "message": "添加成功"}"
// @Success 200 {string} string	"{"code": -1, "message": "添加失败"}"
// @Router /api/menu [post]
func addMenu(ctx *gin.Context) {
	request := new(addRequest)
	if err := ctx.ShouldBindJSON(request); err != nil {
		response.Error(ctx, "参数错误")
		return
	}
	m := &models.Menu{
		MenuName:   request.MenuName,
		Title:      *request.Title,
		Icon:       *request.Icon,
		Path:       request.Path,
		MenuType:   *request.MenuType,
		Action:     request.Action,
		Permission: request.Permission,
		ParentId:   *request.ParentId,
		Component:  request.Component,
		Sort:       *request.Sort,
		Visible:    request.Visible,
		IsFrame:    request.IsFrame,
	}
	err := models.AddMenu(ctx, m)
	if response.ServerError(ctx, err) {
		return
	}
	response.Success(ctx, "添加成功", nil)
}

// @Summary 删除菜单
// @Description 删除菜单
// @Tags menu
// @Param Authorization header string false "Bearer 用户令牌"
// @Param id query int true "id"
// @Success 200 {string} string	"{"code": 200, "message": "删除成功"}"
// @Success 200 {string} string	"{"code": -1, "message": "删除失败"}"
// @Router /api/v1/menu [delete]
func deleteMenu(ctx *gin.Context) {
	id := ctx.Query("id")
	idInt, err := strconv.ParseInt(id, 10, 64)
	if err != nil {
		response.Error(ctx, "参数错误")
		return
	}
	list, err := models.GetMenuList(ctx, "", nil)
	if response.ServerError(ctx, err) {
		return
	}
	menu := models.GetRightMenu(list, idInt)
	if menu == nil {
		response.Error(ctx, "网络异常，请稍后再试")
		return
	}
	ids := []uint64{menu.ID}
	paths := []string{}
	if menu.Path != nil && utf8.RuneCountInString(strings.Trim(*menu.Path, " ")) > 0 {
		paths = append(paths, *menu.Path)
	}
	if len(menu.Children) > 0 {
		i, p := models.GetMenuParam(menu.Children)
		if len(i) > 0 {
			ids = append(ids, i...)
		}
		if len(p) > 0 {
			paths = append(paths, p...)
		}
	}
	err = mysql.DB.Transaction(func(tx *gorm.DB) error {
		if err := models.DeleteMenuByID(tx, ids); err != nil {
			return err
		}
		if err = models.DeleteRoleMenuByMenuID(tx, ids); err != nil {
			return err
		}
		if len(paths) > 0 {
			if err := models.DeletePermissionByPaths(tx, paths); err != nil {
				return err
			}
		}
		return nil
	})
	if response.ServerError(ctx, err) {
		return
	}
	if err := casbin.Permission.LoadPolicy(); err != nil {
		log.Log.Err(err).Send()
	}
	response.Success(ctx, "删除成功", nil)
}

type updateRequest struct {
	addRequest
	ID int64 `json:"id" binding:"required"`
}

// @Summary 修改菜单
// @Description 修改菜单
// @Tags menu
// @Accept  json
// @Product json
// @Param Authorization header string false "Bearer 用户令牌"
// @Param data body updateRequest true "body"
// @Success 200 {string} string	"{"code": 200, "message": "修改成功"}"
// @Success 200 {string} string	"{"code": -1, "message": "添加失败"}"
// @Router /api/menu [put]
func updateMenu(ctx *gin.Context) {
	request := new(updateRequest)
	if err := ctx.ShouldBindJSON(request); err != nil {
		response.Error(ctx, "参数错误")
		return
	}
	existMenu, err := models.GetMenuByID(ctx, request.ID)
	if response.ServerError(ctx, err) {
		return
	}
	m := &models.Menu{
		MenuName:   request.MenuName,
		Title:      *request.Title,
		Icon:       *request.Icon,
		Path:       request.Path,
		MenuType:   *request.MenuType,
		Action:     request.Action,
		Permission: request.Permission,
		ParentId:   *request.ParentId,
		Component:  request.Component,
		Sort:       *request.Sort,
		Visible:    request.Visible,
		IsFrame:    request.IsFrame,
		Model: models.Model{
			ID: uint64(request.ID),
		},
	}
	err = mysql.DB.Transaction(func(tx *gorm.DB) error {
		if err := models.UpdateMenu(tx.WithContext(ctx), m); err != nil {
			return err
		}
		if existMenu.MenuType == models.Api && *request.MenuType == models.Api {
			if err := models.UpdatePermissionByPath(tx, *existMenu.Path, *request.Path, *request.Action); err != nil {
				return err
			}
		}
		return nil
	})
	if err := casbin.Permission.LoadPolicy(); err != nil {
		log.Log.Err(err).Send()
	}
	response.Success(ctx, "修改成功", nil)
}
